/*
 * Copyright (C) 2011 Urals State University,
 * Department of Mathematics and Mechanics
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ru.edu.usu.cs.bubbles.engine.box2d;

import java.util.LinkedList;
import java.util.List;

import ru.edu.usu.cs.bubbles.controllers.DissapearsController;
import ru.edu.usu.cs.bubbles.engine.GameEntity;
import ru.edu.usu.cs.bubbles.engine.GameWorld;
import ru.edu.usu.cs.bubbles.engine.box2d.colliders.Box2dFanCollider;
import ru.edu.usu.cs.bubbles.engine.box2d.colliders.Box2dFishCollider;
import ru.edu.usu.cs.bubbles.engine.box2d.colliders.Box2dSpikeCollider;
import ru.edu.usu.cs.bubbles.engine.box2d.colliders.Box2dWhirlpoolCollider;
import ru.edu.usu.cs.bubbles.engine.box2d.colliders.Collider;
import ru.edu.usu.cs.bubbles.representations.BubbleRepresentation;
import ru.edu.usu.cs.bubbles.representations.IceWallRepresentation;
import ru.edu.usu.cs.bubbles.representations.WallRepresentation;
import ru.edu.usu.cs.bubbles.representations.WhirlpoolRepresentation;
import ru.edu.usu.cs.bubbles.utils.Point;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.World;

public class Box2dGameWorld extends GameWorld {
	static {
		System.loadLibrary("gdx");
	}

	private World world;
	private List<GameEntity> entities = new LinkedList<GameEntity>();
	private List<GameEntity> toDestroyEntities = new LinkedList<GameEntity>();

	public Box2dGameWorld() {
		world = new World(new Vector2(0, 1f), true);
	}

	public Box2dGameWorld(Point gravity) {
		world = new World(new Vector2(gravity), true);
	}

	@Override
	public List<GameEntity> getEntities() {
		return entities;
	}

	@Override
	public void step(float time) {
		world.step(time, 10, 10);

		for (Contact contact : world.getContactList()) {
			Fixture firstFixture = contact.getFixtureA();
			Fixture secondFixture = contact.getFixtureB();

			Collider firstCollider = firstFixture.getGameEntity().getCollider();
			Collider secondCollider = secondFixture.getGameEntity()
					.getCollider();
			if (firstCollider != null)
				firstCollider.processCollision(firstFixture, secondFixture);
			if (secondCollider != null)
				secondCollider.processCollision(firstFixture, secondFixture);
		}
	}

	@Override
	public Box2dBubble createBubble(float xPos, float yPos, float radius) {
		BodyDef bd = new BodyDef();
		bd.position.set(xPos, yPos);
		Body body = world.createBody(bd);
		Box2dBubble bubble = new Box2dBubble(body, radius);
		bubble.representation = new BubbleRepresentation(bubble);
		entities.add(bubble);
		return bubble;
	}

	@Override
	public Box2dFan createFan(float xCenter, float yCenter, float power,
			float angle) {
		BodyDef bd = new BodyDef();
		bd.position.set(xCenter, yCenter);
		Body body = world.createBody(bd);

		Box2dFan fan = new Box2dFan(body, power, angle);
		fan.setCollider(new Box2dFanCollider(this));
		entities.add(fan);
		return fan;
	}

	@Override
	public Box2dFish createFish(float xCenter, float yCenter, float angle) {
		BodyDef fisheBodyDef = new BodyDef();
		fisheBodyDef.position.set(xCenter, yCenter);
		Body fishBody = world.createBody(fisheBodyDef);

		Box2dFish fish = new Box2dFish(fishBody, angle);
		fish.setCollider(new Box2dFishCollider(this));
		entities.add(fish);
		return fish;
	}

	@Override
	public Box2dSpike createSpike(float xCenter, float yCenter, float angle) {
		BodyDef spikeBodyDef = new BodyDef();
		spikeBodyDef.position.set(xCenter, yCenter);
		Body spikeBody = world.createBody(spikeBodyDef);

		Box2dSpike spike = new Box2dSpike(spikeBody, angle);
		spike.setCollider(new Box2dSpikeCollider(this));
		entities.add(spike);
		return spike;
	}

	@Override
	public Box2dWall createWall(float length, float width, Point center,
			float angle) {
		BodyDef wallBodyDef = new BodyDef();
		wallBodyDef.position.set(center.x, center.y);
		Body wallBody = world.createBody(wallBodyDef);

		Box2dWall wall = new Box2dWall(wallBody, length, width, angle);
		wall.representation = new WallRepresentation(wall);
		entities.add(wall);
		return wall;
	}

	public Box2dWall createIceWall(float length, float width, Point center,
			float angle) {
		Box2dWall wall = createWall(length, width, center, angle);
		wall.representation = new IceWallRepresentation(wall);
		//TODO перенести TTL для стены в параметры и поставить зависимость от сложности или от уровня
		wall.controller = new DissapearsController(wall, 30);
		return wall;
	}

	@Override
	public Box2dWhirlpool createWhirlpool(float xPos, float yPos, float radius,
			float power) {
		BodyDef whirlDef = new BodyDef();
		whirlDef.position.set(xPos, yPos);
		Body body = world.createBody(whirlDef);

		Box2dWhirlpool whirlpool = new Box2dWhirlpool(body, radius, power);
		whirlpool.representation = new WhirlpoolRepresentation(whirlpool);
		whirlpool.setCollider(new Box2dWhirlpoolCollider(this));
		entities.add(whirlpool);
		return whirlpool;
	}

	public void markDestroy(GameEntity entity) {
		toDestroyEntities.add(entity);
	}

	public void destoyAllMarked() {
		synchronized (toDestroyEntities) {
			for (GameEntity entity : toDestroyEntities) {
				entity.representation.playDestructionAnimation();
				world.destroyBody(((Box2dEntity) entity).getBody());
				entities.remove(entity);
			}
			toDestroyEntities.clear();
		}
	}

	@Override
	public void destroyEntity(GameEntity entity) {
		entity.representation.playDestructionAnimation();
		world.destroyBody(((Box2dEntity) entity).getBody());
		entities.remove(entity);
	}
}
